package com.mallcai.bigdata.ladon.datasource;

import com.cake.bigdata.starter.exception.category.BusinessException;
import com.mallcai.bigdata.ladon.config.ApplicationContextProvider;
import com.mallcai.bigdata.ladon.dpl.entity.common.Datasource;
import com.mallcai.bigdata.ladon.dpl.entity.report.SqlRunLog;
import com.mallcai.bigdata.ladon.service.report.ISqlRunLogQueueService;
import com.mallcai.bigdata.ladon.service.report.impl.handler.support.OldMapParamPreparedStatementSetter;
import com.mallcai.bigdata.ladon.utils.DataSourceUtils;
import com.mallcai.bigdata.ladon.utils.ExceptionUtils;
import com.mallcai.bigdata.ladon.vo.DataVo;
import com.mallcai.bigdata.ladon.vo.QueryParam;
import com.zaxxer.hikari.HikariDataSource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;

/**
 * Created by oneape<oneape15@163.com>
 * Created 2019-06-10 20:34.
 * Modify:
 */
@Slf4j
public class MySQLDataSource extends AbstractDataSource {


    public MySQLDataSource(Datasource ds) {
        super(ds);
    }

    @Override
    public DataSource initDataSource(Datasource ds) {
        HikariDataSource hds = new HikariDataSource();
        hds.setMaximumPoolSize(10);
        hds.setDriverClassName(ds.getJdbcDriver());
        hds.setJdbcUrl(ds.getJdbcUrlWithSocketTimeout());
        hds.setUsername(ds.getUsername());
        hds.setPassword(ds.getPassword());
        hds.setAutoCommit(true);

        try {
            hds.setLoginTimeout(3);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return hds;
    }

    /**
     * 获取连接对象.
     *
     * @return Connection
     */
    @Override
    public Connection getConnection() throws BusinessException {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            log.error("获取连接信息:失败",e);
            throw new BusinessException("获取连接信息失败", e);
        }
    }

    /**
     * 是否支持分页
     *
     * @return boolean
     */
    @Override
    public boolean supportPage() {
        return true;
    }

    /**
     * 执行查询语句
     *
     * @param queryParam QueryParam
     * @return DataVo
     */
    @Override
    public DataVo execSql(QueryParam queryParam) throws BusinessException {
        String sql = queryParam.getRawSql();
        if (StringUtils.isBlank(sql)) {
            throw new BusinessException("查询SQL为空");
        }

        //判断在sql的最后面是否有LIMIT存在
        boolean isExistLimit = isEndWithLimit(sql);
        int limitSize = ONE_MAX_SIZE;
        if (queryParam.getLimitSize() != null) {
            limitSize = queryParam.getLimitSize();
        }
        if (isExistLimit) {
            sql = editLimitIfOverMax(sql);
        }
        if (!isExistLimit && queryParam.isNeedLimit()) {
            sql = sql + " " + KEY_LIMIT + limitSize;
        }

        long timeElapsed = -1;
        Exception error = null;

        Map<Integer, Object> params = queryParam.getParams();

        PreparedStatement preStmt = null;
        ResultSet resultSet = null;
        Connection conn = getConnection();
        try {
            preStmt = conn.prepareStatement(sql);
            OldMapParamPreparedStatementSetter mapParamPreparedStatementSetter = new OldMapParamPreparedStatementSetter(params, queryParam.getTimeout());
            mapParamPreparedStatementSetter.setValues(preStmt);
            log.debug("开始执行sql!!");
            //执行sql
            long startTime = System.nanoTime();
            try{
                resultSet = preStmt.executeQuery();
            }finally {
                long endTime = System.nanoTime();
                timeElapsed = (endTime - startTime) / NS_MS;  //将纳秒 转换成 毫秒
                //超过800ms的记录sql
                if (timeElapsed > 800) {
                    log.warn("耗时:{}ms, 执行sql:{}", timeElapsed, preStmt.toString());
                }
            }
            //获取列表对象
            DataVoExtractor dataVoExtractor = new DataVoExtractor();
            return dataVoExtractor.extractData(resultSet);
        } catch (Exception e) {
            error = e;
            throw new BusinessException("查询运行错误", e);
        } finally {
            SqlRunLog sqlRunLog = queryParam.getSqlRunLog();
            if (sqlRunLog != null){
                sqlRunLog.setText(sql);
                sqlRunLog.setElapsedTime((int)timeElapsed);
                if(error != null){
                    sqlRunLog.setComplete(0);
                    sqlRunLog.setErrInfo(ExceptionUtils.buildErrorMessage(error));
                } else {
                    sqlRunLog.setComplete(1);
                }
                doLog(sqlRunLog);
            }
            DataSourceUtils.release(resultSet, preStmt, conn);
        }
    }


    private void doLog(SqlRunLog sqlRunLog){
        ISqlRunLogQueueService queueService = ApplicationContextProvider.getAppCtx().getBean(ISqlRunLogQueueService.class);
        if (queueService != null){
            queueService.putLog(sqlRunLog);
        }
    }
}
